#include "internal.h"
#include "osip_mt.h"


osip_mutex::osip_mutex(void)
{
#ifndef WIN32
	pthread_mutex_init(&m_event_mutex, 0);
#else
	InitializeCriticalSection(&m_event_mutex);
#endif
}

osip_mutex::~osip_mutex(void)
{
#ifdef WIN32
	DeleteCriticalSection(&m_event_mutex);
#else
	pthread_mutex_destroy(&m_event_mutex);
#endif
}

void osip_mutex::enter_mutex()
{
#ifdef WIN32
	EnterCriticalSection(&m_event_mutex);
#else
	pthread_mutex_lock(&m_event_mutex);
#endif
}

void osip_mutex::leave_mutex()
{
#ifdef WIN32
	LeaveCriticalSection(&m_event_mutex);
#else
	pthread_mutex_unlock(&m_event_mutex);
#endif
}


osip_sem::osip_sem()
{
#ifdef WIN32
	_sem = CreateSemaphore(0, 0, 0x100, 0);
#else
	sem_init(&_sem, 0, 0);
#endif
}

osip_sem::~osip_sem()
{
#ifdef WIN32
	CloseHandle(_sem);
#else
	sem_destroy(&_sem);
#endif
}

void osip_sem::sem_signal()
{
#ifdef WIN32
	ReleaseSemaphore(_sem, 1, NULL);
#else
	sem_post(&_sem);
#endif
}

bool osip_sem::sem_objwait()
{
#ifdef WIN32
	return (WAIT_OBJECT_0 == WaitForSingleObject(_sem, INFINITE));
#else
	return (0 == sem_wait(&_sem));
#endif
}

bool osip_sem::sem_timewait(int msec)
{
#ifdef WIN32
	return (WAIT_OBJECT_0 == ::WaitForSingleObject(_sem, msec));
#else

	int error = 0;
	struct timeval tv;
	struct timespec ts;

	gettimeofday(&tv, 0);
	ts.tv_sec = tv.tv_sec + (msec / 1000);
	ts.tv_nsec = tv.tv_usec * 1000 + (msec % 1000) * 1000000;

	// Handle overflow.
	if (ts.tv_nsec >= 1000000000) {
		ts.tv_sec++;
		ts.tv_nsec -= 1000000000;
	}

	return (0 == sem_timedwait(&_sem, &ts));
#endif
}

osip_event::osip_event(void)
{
#ifndef WIN32
	m_hasnal = false;
	pthread_cond_init(&m_event_cond, 0);
#else
	m_event_cond = CreateEvent(0, false, false, 0);
#endif
}

osip_event::~osip_event(void)
{
#ifdef WIN32
	SAFE_CLOSEHD(m_event_cond);
#else
	pthread_cond_destroy(&m_event_cond);
#endif
}

void osip_event::event_signal()
{
#ifdef WIN32
	SetEvent(m_event_cond);
#else
	pthread_mutex_lock(&m_event_mutex);
	m_hasnal = true;
	pthread_cond_broadcast(&m_event_cond);
	pthread_mutex_unlock(&m_event_mutex);
#endif
}

bool osip_event::event_objwait()
{
#ifdef WIN32
	if (WaitForSingleObject(m_event_cond, INFINITE) == WAIT_OBJECT_0) {
		return true;
	}
	return false;
#else
	int error = 0;
	pthread_mutex_lock(&m_event_mutex);

	while (!m_hasnal && error == 0) {
		error = pthread_cond_wait(&m_event_cond, &m_event_mutex);
	}

	if (error == 0) {
		m_hasnal = false;
	}

	pthread_mutex_unlock(&m_event_mutex);

	return (error == 0) ? true : false;
#endif
}

bool osip_event::event_timewait(int msec)
{
#ifdef WIN32
	if (WaitForSingleObject(m_event_cond, msec) == WAIT_OBJECT_0) {
		ResetEvent(m_event_cond);
		return true;
	}
	ResetEvent(m_event_cond);
	return false;
#else
	// Converting from seconds and microseconds (1e-6) plus
	// milliseconds (1e-3) to seconds and nanoseconds (1e-9).
	int error = 0;
	struct timeval tv;
	struct timespec ts;

	gettimeofday(&tv, 0);
	ts.tv_sec = tv.tv_sec + (msec / 1000);
	ts.tv_nsec = tv.tv_usec * 1000 + (msec % 1000) * 1000000;

	// Handle overflow.
	if (ts.tv_nsec >= 1000000000) {
		ts.tv_sec++;
		ts.tv_nsec -= 1000000000;
	}

	pthread_mutex_lock(&m_event_mutex);

	while (!m_hasnal && error == 0) {
		error = pthread_cond_timedwait(&m_event_cond, &m_event_mutex, &ts);
	}

	if (error == 0) {
		m_hasnal = false;
	}

	pthread_mutex_unlock(&m_event_mutex);

	return (error == 0) ? true : false;
#endif
}


CAutoMutex::CAutoMutex(osip_mutex* cirt)
	: m_stCS(*cirt)
{
	cirt->enter_mutex();
}

CAutoMutex::CAutoMutex(osip_mutex& cirt)
	: m_stCS(cirt)
{
	cirt.enter_mutex();
}

CAutoMutex::~CAutoMutex()
{
	m_stCS.leave_mutex();
}
